package bancosys.tec.services.implementor.descriptor;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

import jmine.tec.di.scope.TypedScope;
import jmine.tec.services.annotations.Validation;
import jmine.tec.services.api.ServiceExecutionException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * Cria um {@link ServiceValidator}
 * 
 * @author MarceloT
 */
class ValidatorDescriptor {

    /**
     * @author MarceloT
     */
    private static final class ServiceValidatorImpl implements ServiceValidator {
        private final List<Method> selected;

        private final Log logger = LogFactory.getLog(this.getClass().getName());

        /**
         * C'tor
         * 
         * @param selected {@link List} of {@link Method}
         */
        private ServiceValidatorImpl(List<Method> selected) {
            this.selected = selected;
            for (Method m : selected) {
                m.setAccessible(true);
            }
        }

        /**
         * Valida
         * 
         * @param target Object
         * @param properties {@link TypedScope}
         * @return {@link List} de {@link String}
         */
        public List<String> validate(Object target, TypedScope properties) {
            List<String> list = new ArrayList<String>();
            for (Method m : this.selected) {
                try {
                    m.invoke(target);
                } catch (IllegalAccessException e) {
                    throw new IllegalStateException(e);
                } catch (InvocationTargetException e) {
                    Throwable cause = e.getTargetException();
                    if (cause instanceof Error) {
                        throw (Error) cause;
                    }
                    if (cause instanceof RuntimeException) {
                        if (m.getAnnotation(Validation.class).validateRuntimeException()) {
                            this.logger.info("runtimeexception catched as validation error", cause);
                            list.add(cause.getMessage());
                        } else {
                            throw (RuntimeException) cause;
                        }
                    } else {
                        this.logger.info("application exception thrown", cause);
                        list.add(cause.getMessage());
                    }
                }
            }
            return list;
        }

        /**
         * @return the selected
         */
        public List<Method> getValidationMethods() {
            return new ArrayList<Method>(this.selected);
        }

        /**
         * {@inheritDoc}
         */
        public void validateThrowException(Object target, TypedScope scope) throws ServiceExecutionException {
            for (Method m : this.selected) {
                try {
                    m.invoke(target);
                } catch (IllegalAccessException e) {
                    throw new IllegalStateException(e);
                } catch (InvocationTargetException e) {
                    Throwable cause = e.getTargetException();
                    if (cause instanceof Error) {
                        throw (Error) cause;
                    }
                    if (cause instanceof RuntimeException) {
                        if (m.getAnnotation(Validation.class).validateRuntimeException()) {
                            this.logger.info("runtimeexception catched as validation error", cause);
                            throw new ServiceExecutionException(cause);
                        } else {
                            throw (RuntimeException) cause;
                        }
                    } else if (cause instanceof ServiceExecutionException) {
                        throw (ServiceExecutionException) cause;
                    } else {
                        this.logger.info("application exception thrown", cause);
                        throw new ServiceExecutionException(cause);
                    }
                }
            }
        }
    }

    /**
     * Cria um {@link ServiceValidator}
     * 
     * @param type type
     * @return {@link ServiceValidator}
     */
    public ServiceValidator createValidatorFor(Class<?> type) {
        Method[] methods = type.getMethods();
        final List<Method> selected = new ArrayList<Method>();
        for (Method method : methods) {
            if (method.isAnnotationPresent(Validation.class) && method.getParameterTypes().length == 0) {
                selected.add(method);
            }
        }
        return new ServiceValidatorImpl(selected);
    }

}
